package com.xy.smartsms.facade;

import android.app.Activity;
import android.content.Context;
import android.text.TextUtils;

import com.xy.smartsms.data.ISmsPlugin;
import com.xy.smartsms.data.PublicInfoData;
import com.xy.smartsms.data.SmsItem;
import com.xy.smartsms.db.carrierparam.RecognitionErrManager;
import com.xy.smartsms.db.carrierparam.entity.RecognitionErrorItem;
import com.xy.smartsms.manager.PublicInfoManager;
import com.xy.smartsms.util.CommonUtils;
import com.xy.smartsms.util.JsonUtil;
import com.xy.smartsms.util.Log;

import org.json.JSONArray;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import cn.com.xy.sms.sdk.action.AbsSdkDoAction;
import cn.com.xy.sms.sdk.util.DuoquUtils;
import cn.com.xy.sms.util.SdkCallBack;

/**
 * 提供统一的卡片/菜单识别接口的外观类
 *
 * @author XIAOYUAN
 * @version 2.0
 */
public class SmsProcessFacade {
    private static final String TAG = SmsProcessFacade.class.getSimpleName();
    private static ExecutorService sCachePool = Executors.newFixedThreadPool(1);
    private static ExecutorService sPubicInfoPool = Executors.newFixedThreadPool(1);
    private Context mContext = null;
    private boolean mSafeCode = false;
    private AbsSdkDoAction mDoAction = null;
    private Map<String, String> mInitMap = new HashMap<>();

    /**
     * 设置运营商识别超时时间，单位ms，不设置默认6000
     */
    public static final String KEY_PARSE_TIMEOUT = "custom_parse_time_out";
    /**
     * 设置使能安全码（不显示卡片）
     */
    public static final String KEY_SAFE_CODE = "enable_safe_code";
    /**
     * 设置默认使用的识别引擎
     */
    public static final String KEY_TOP_SWITCH = "top_switch_carrier";
    /**
     * 设置是否需要使用新的企业资料接口
     */
    public static final String KEY_PUBLIC_INFO = "new_public_info";
    /**
     * 设置是否需要使能远程识别
     */
    public static final String KEY_REMOTE_PARSE_ENABLE = "remote_parse_enable";
    /**
     * 设置提供远程服务的包名；默认为"com.xy.bizport"
     */
    public static final String KEY_REMOTE_PARSE_PACKAGE = "remote_parse_package";
    /**
     * 设置提供远程服务的ACTION名；默认为"com.xy.bizport.service.aidl.RemoteCallableStub"
     */
    public static final String KEY_REMOTE_PARSE_ACTION = "remote_parse_action";

    static private class CardResult {
        private int status;
        private int type;
        private JSONObject data;
        private long parseTime;
        private String sceneId;

        public JSONObject getData() {
            return data;
        }

        public CardResult setData(JSONObject data) {
            this.data = data;
            return this;
        }

        public long getParseTime() {
            return parseTime;
        }

        public int getStatus() {
            return status;
        }

        public CardResult setStatus(int status) {
            this.status = status;
            return this;
        }

        public int getType() {
            return type;
        }

        public CardResult setType(int type) {
            this.type = type;
            return this;
        }

        public CardResult setParseTime(long parseTime) {
            this.parseTime = parseTime;
            return this;
        }

        public String getSceneId() {
            return sceneId;
        }

        public CardResult setSceneId(String sceneId) {
            this.sceneId = sceneId;
            return this;
        }
    }

    static private class MenuResult {
        private int status;
        private long parseTime;
        private PublicInfoData data;

        public PublicInfoData getData() {
            return data;
        }

        public MenuResult setData(PublicInfoData data) {
            this.data = data;
            return this;
        }

        public long getParseTime() {
            return parseTime;
        }

        public MenuResult setParseTime(long parseTime) {
            this.parseTime = parseTime;
            return this;
        }

        public int getStatus() {
            return status;
        }

        public MenuResult setStatus(int status) {
            this.status = status;
            return this;
        }
    }

    /**
     * 卡片识别回调
     */
    public interface ISmsProcessCallback {
        /**
         * 卡片识别结果回调
         *
         * @param status   识别状态 @see ISmsPlugin.STATUS_SUCCESS ISmsPlugin.STATUS_FAIL
         * @param type     待识别卡片类型 @see ISmsPlugin.TYPE_RICH
         * @param cacheKey 当前识别结果的唯一识别ID
         * @param obj      识别结果
         * @note 识别状态即使为SUCCESS，识别结果也可能为空
         */
        void onCard(int status, int type, String cacheKey, JSONObject obj);
    }

    /**
     * 菜单/企业资料 识别回调
     */
    public interface ISmsProcessPublicInfoCallback {
        /**
         * 菜单识别结果回调
         *
         * @param phone 接入号码
         * @param data  识别结果
         */
        void onPublicInfo(String phone, PublicInfoData data);
    }

    private RecognitionErrorItem mapCardResultToItem(CardResult result) {
        RecognitionErrorItem item = new RecognitionErrorItem()
                .setRecognise(result.getData() != null)
                .setRecogniseTime(String.valueOf(result.getParseTime()))
                .setScene(result.getSceneId());
        return item;
    }

    private void reportCustomCapacity(CardResult defaultResult, CardResult customResult) {
        Log.v(TAG, "reportCustomCapacity Card: " + " Default: " + defaultResult.getParseTime() + " Custom:" +
                customResult.getParseTime());
        RecognitionErrorItem defaultItem = mapCardResultToItem(defaultResult).setSdkType(0);
        RecognitionErrorItem customItem = mapCardResultToItem(customResult);
        RecognitionErrManager.getInstance(mContext).insertSceneErr(defaultItem, customItem);
    }

    private RecognitionErrorItem mapMenuResultToItem(MenuResult result) {
        RecognitionErrorItem item = new RecognitionErrorItem()
                .setRecognise(result.getData() != null && result.getData().json != null)
                .setRecogniseTime(String.valueOf(result.getParseTime()));
        return item;
    }

    private void reportCustomCapacity(MenuResult defaultResult, MenuResult customResult) {
        Log.v(TAG, "reportCustomCapacity Menu: " + " Default: " + defaultResult.getParseTime() + " Custom:" + customResult
                .getParseTime());
        RecognitionErrorItem defaultItem = mapMenuResultToItem(defaultResult).setSdkType(0);
        RecognitionErrorItem customItem = mapMenuResultToItem(customResult);
        RecognitionErrManager.getInstance(mContext).insertMenuErr(defaultItem, customItem);
    }

    private boolean useDefaultCard(SmsItem item, String sceneId) {
        return sceneId != null
                && mCarrierParamFacade.checkBlackListSwitch(item.getPhone(), CarrierParamFacade.FUNC_CARD_SCENE)
                && mCarrierParamFacade.checkEscapeScene(sceneId);
    }

    private boolean useDefaultCard(SmsItem item, JSONObject obj) {
        if (isCustomOnly(CarrierParamFacade.FUNC_CARD)) {
            return false;
        }

        return mCarrierParamFacade.checkBlackListSwitch(item.getPhone(), CarrierParamFacade.FUNC_CARD)
                || mCarrierParamFacade.checkBlackListSwitch(item.getPhone(), CarrierParamFacade.FUNC_CARD_SCENE)
                || (obj == null && mCarrierParamFacade.checkOnResultNull(CarrierParamFacade.FUNC_CARD));
    }

    private void initCarrierPlugin() {
        if (SmsPluginManager.getInstance().getCustom() != null) {
            return;
        }

        if (SmsPluginManager.getInstance().loadCarrierPlugin()) {
            SmsPluginManager.getInstance().getCustom().init(new ISmsPlugin.ISmsPluginAction() {
                @Override
                public List<SmsItem> getMessageList(String phone, long startTime, long endTime, int limit) {
                    List<JSONObject> jsonList = mDoAction.getReceiveMsgByReceiveTime(phone, startTime, endTime, limit);
                    if (jsonList == null) {
                        return null;
                    }

                    List<SmsItem> itemList = new ArrayList<SmsItem>();
                    for (JSONObject json : jsonList) {
                        String msgId = (String) JsonUtil.getValFromJsonObject(json, "msgId");
                        long receiveTime = Long.valueOf((String) JsonUtil.getValFromJsonObject(json, "smsReceiveTime"));
                        SmsItem item = new SmsItem().setMsgId(msgId)
                                .setBody((String) JsonUtil.getValFromJsonObject(json, "msg"))
                                .setPhone((String) JsonUtil.getValFromJsonObject(json, "phone"))
                                .setCacheKey(CommonUtils.getCacheKey(msgId, receiveTime))
                                .setReceiveTime(receiveTime);
                        itemList.add(item);
                    }

                    return itemList;
                }
            }, mInitMap);
        }
    }

    private int checkTopSwitch(int funcType) {
        int topSwitch = AbsFacadeDoAction.SWITCH_NA;
        if (mDoAction instanceof AbsFacadeDoAction) {
            topSwitch = ((AbsFacadeDoAction) mDoAction).customTopSwitch();
        }
        int result;
        switch (topSwitch) {
            case AbsFacadeDoAction.SWITCH_CARRIER:
                result = CarrierParamFacade.TOP_SWITCH_OPERATOR;
                break;

            case AbsFacadeDoAction.SWITCH_XIAOYUAN:
                result = CarrierParamFacade.TOP_SWITCH_XY;
                break;

            case AbsFacadeDoAction.SWITCH_COMBINE:
                result = CarrierParamFacade.TOP_SWITCH_MIX;
                mCarrierParamFacade.checkUpdate();
                break;

            default:
                result = mCarrierParamFacade.checkTopSwitch(funcType);
                mCarrierParamFacade.checkUpdate();
                break;
        }

        Log.v(TAG, "checkTopSwitch: " + result);
        if (result != CarrierParamFacade.TOP_SWITCH_XY) {
            initCarrierPlugin();
        }

        return result;
    }

    private boolean isDefaultOnly(int funcType) {
        return checkTopSwitch(funcType) == CarrierParamFacade.TOP_SWITCH_XY;
    }

    private boolean isCustomOnly(int funcType) {
        return checkTopSwitch(funcType) == CarrierParamFacade.TOP_SWITCH_OPERATOR;
    }

    private JSONObject handleCustomVerifyCode(JSONObject result) {
        if (result == null
                || !(result.has("custom_card_code"))
                || !mSafeCode) {
            return result;
        }

        result.remove("View_fdes");
        if ("password".equals(result.opt("custom_card_code"))) {
            result.remove("NEW_ADACTION");
        }

        return result;
    }

    private JSONObject buildPrimitiveData(SmsItem item) {
        JSONObject obj = null;
        try {
            // 短信原文布局
            obj = new JSONObject();
            obj.put("View_fdes", "H450;B551;F950");
            obj.put("view_title_name",
                    mCarrierParamFacade.getAdvancedWhiteListName(item.getPhone()));
            // use this key to indicate this card need to rebind
            obj.put("custom_card_code", "default");
        } catch (Throwable t) {
            Log.e(TAG, "advanced white list", t);
        }

        return obj;
    }

    private void getCustomCard(final int type,
                               final SmsItem item,
                               final CardResult defaultResult,
                               final ISmsProcessCallback callback) {
        ISmsPlugin plugin = SmsPluginManager.getInstance().getCustom(getOperator(item));
        if (useDefault(type) || plugin == null) {
            return;
        } else if (type == ISmsPlugin.TYPE_SIMPLE) {
            if (callback != null) {
                callback.onCard(ISmsPlugin.STATUS_SUCCESS, type, item.getCacheKey(), null);
            }

            return;
        }

        final boolean needPrimitiveData = "data".equals(plugin.getParam("custom_plugin_type"));
        final long start = System.currentTimeMillis();
        Log.i(TAG, "getCustomCard: " + " type=" + type + " id=" + item.getMsgId());
        plugin.parseCard(type,
                item,
                new ISmsPlugin.ISmsPluginCallback() {
                    @Override
                    public void onCard(int status, int type, JSONObject obj) {
                        Log.d(TAG, "getCustomCard: " + " type=" + type + " status =" + status + " obj=" + obj);
                        if (status == ISmsPlugin.STATUS_FAIL) {
                            if (callback != null) {
                                if (mCarrierParamFacade.checkAdvancedWhiteList(item.getPhone())
                                        && needPrimitiveData) {
                                    status = ISmsPlugin.STATUS_SUCCESS;
                                    obj = buildPrimitiveData(item);
                                }

                                callback.onCard(status, type, item.getCacheKey(), obj);
                            }

                            return;
                        }

                        long end = System.currentTimeMillis();
                        CardResult customResult = new CardResult().setStatus(status)
                                .setParseTime(end - start)
                                .setData(obj)
                                .setType(type);
                        ISmsProcessCallback defaultCallback = null;
                        if (callback != null) {
                            if (mCarrierParamFacade.checkAdvancedWhiteList(item.getPhone())) {
                                if (obj == null && needPrimitiveData) {
                                    obj = buildPrimitiveData(item);
                                }

                                callback.onCard(status, type, item.getCacheKey(), obj);
                            } else if (useDefaultCard(item, obj)) {
                                defaultCallback = callback;
                            } else {
                                callback.onCard(status, type, item.getCacheKey(), handleCustomVerifyCode(obj));
                            }
                        }

                        if (defaultResult != null) {
                            reportCustomCapacity(defaultResult, customResult);
                        } else if (!isCustomOnly(CarrierParamFacade.FUNC_CARD)) {
                            getDefaultCard(type, item, customResult, defaultCallback);
                        }
                    }

                    @Override
                    public void onPublicInfo(int status, PublicInfoData data) {

                    }
                });

    }

    private void getDefaultCard(final int type,
                                final SmsItem item,
                                final CardResult customResult,
                                final ISmsProcessCallback callback) {
        final long start = System.currentTimeMillis();
        Log.i(TAG, "getDefaultCard: " + " type=" + type + " id=" + item.getMsgId());
        SmsPluginManager.getInstance().getDefault().parseCard(type, item, new ISmsPlugin.ISmsPluginCallback() {
            @Override
            public void onCard(int status, int type, JSONObject obj) {
                Log.d(TAG, "getDefaultCard: " + " type=" + type + " status =" + status + " obj=" + obj);
                if (status == ISmsPlugin.STATUS_FAIL) {
                    if (callback != null) {
                        callback.onCard(status, type, item.getCacheKey(), obj);
                    }

                    return;
                }

                String sceneId = null;
                if (obj != null) {
                    if (type == ISmsPlugin.TYPE_SIMPLE) {
                        JSONArray array = obj.optJSONArray("data");
                        if (array != null) {
                            JSONObject button = array.optJSONObject(0);
                            if (button != null) {
                                sceneId = button.optString("sceneNum");
                            }
                        }
                    } else {
                        sceneId = obj.optString("title_num");
                    }
                }

                if (callback != null) {
                    if (customResult != null
                            && customResult.getData() != null
                            && !useDefaultCard(item, sceneId)) {
                        // CARD / CARD_SCENE both set to false
                        callback.onCard(customResult.getStatus(),
                                customResult.getType(),
                                item.getCacheKey(),
                                customResult.getData());
                    } else {
                        callback.onCard(status, type, item.getCacheKey(), obj);
                    }
                }

                long end = System.currentTimeMillis();
                CardResult defaultResult = new CardResult().setStatus(status)
                        .setParseTime(end - start)
                        .setData(obj)
                        .setType(type)
                        .setSceneId(sceneId);
                if (customResult != null) {
                    reportCustomCapacity(defaultResult, customResult);
                } else if (!(isDefaultOnly(CarrierParamFacade.FUNC_CARD) || getCustomSelect(item))) {
                    getCustomCard(type, item, defaultResult, null);
                }
            }

            @Override
            public void onPublicInfo(int status, PublicInfoData data) {

            }
        });
    }

    private boolean isParamReady(int funcType, String phone) {
        if (isCustomOnly(funcType) || isDefaultOnly(funcType)) {
            return true;
        }

        return mCarrierParamFacade.isParamReady(phone);
    }

    /**
     * 异步获取卡片数据
     *
     * @param type     卡片类型 @see ISmsPlugin.TYPE_RICH ISmsPlugin.TYPE_SIMPLE ISmsPlugin.TYPE_FEATURE
     * @param item
     * @param callback
     * @remark item 的所有项都必须要正确设置
     */
    public void parseCard(final int type, final SmsItem item, final ISmsProcessCallback callback) {
        if (!isParamReady(CarrierParamFacade.FUNC_CARD, item.getPhone())) {
            if (callback != null) {
                callback.onCard(ISmsPlugin.STATUS_FAIL, type, item.getCacheKey(), null);
            }

            Log.i(TAG, "parseCard: param not ready");
            return;
        }

        //TODO Default=xy; custom=(cmcc,->xy)
        if (useDefault(item.getPhone(), CarrierParamFacade.FUNC_CARD, getCustomSelect(item))
                || useDefault(type) //简单，特症值
                || SmsPluginManager.getInstance().getCustom(getOperator(item)) == null) {
            getDefaultCard(type, item, null, callback);
        } else {
            getCustomCard(type, item, null, callback);
        }
    }

    public void parseCardSync(final int type, final SmsItem item) {
        SmsPluginManager.getInstance().getDefault().parseCardSync(type, item);
        if (SmsPluginManager.getInstance().getCustom(getOperator(item)) != null) {
            SmsPluginManager.getInstance().getCustom(getOperator(item)).parseCardSync(type, item);
        }
    }

    private int getOperator(SmsItem item) {
        int simIndex = -1;
        String iccid = null;
        if (item != null) {
            simIndex = item.getSimIndex();
            iccid = item.getIccid();
        }

        return DuoquUtils.getSdkDoAction().getModelOperator(simIndex, iccid);
    }

    private boolean getCustomSelect(Map<String, String> extend) {
        int simIndex = -1;
        String iccid = null;
        if (extend != null) {
            String value = extend.get("simIndex");
            if (!TextUtils.isEmpty(value)) {
                simIndex = Integer.parseInt(value);
            }

            iccid = extend.get("iccid");
        }

        int operator = DuoquUtils.getSdkDoAction().getModelOperator(simIndex, iccid);
        return operator == AbsSdkDoAction.OPERATOR_UNKNOWN;
    }

    private boolean getCustomSelect(SmsItem item) {
        return getOperator(item) == AbsSdkDoAction.OPERATOR_UNKNOWN;
    }

    private boolean useDefault(int type) {
        return type == ISmsPlugin.TYPE_FEATURE || type == ISmsPlugin.TYPE_SENSITIVE;
    }

    private boolean useDefault(String phone, int funcType, boolean customSelect) {
        if (isCustomOnly(funcType)) {
            return false;
        }

        return (isDefaultOnly(funcType)
                || customSelect
                || !mCarrierParamFacade.checkWhiteList(phone)
                || (!mCarrierParamFacade.checkAdvancedWhiteList(phone) && mCarrierParamFacade.checkBlackListSwitch(phone, funcType)));
    }

    private void getDefaultMenu(final String phone,
                                final Map<String, String> extend,
                                final MenuResult customResult,
                                final ISmsProcessPublicInfoCallback callback) {
        final long start = System.currentTimeMillis();
        Log.i(TAG, "getDefaultMenu: " + " phone=" + phone);
        SmsPluginManager.getInstance().getDefault().parsePublicInfo(phone,
                extend,
                new ISmsPlugin.ISmsPluginCallback() {
                    @Override
                    public void onCard(int status, int type, JSONObject obj) {

                    }

                    @Override
                    public void onPublicInfo(int status, PublicInfoData data) {
                        if (callback != null) {
                            callback.onPublicInfo(phone, data);
                        }

                        long end = System.currentTimeMillis();
                        MenuResult defaultResult = new MenuResult().setData(data)
                                .setParseTime(end - start)
                                .setStatus(status);
                        if (customResult != null) {
                            reportCustomCapacity(defaultResult, customResult);
                        } else if (!(isDefaultOnly(CarrierParamFacade.FUNC_MENU) || getCustomSelect(extend))) {
                            getCustomMenu(phone, extend, defaultResult, null);
                        }
                    }
                });
    }

    private void getCustomMenu(final String phone,
                               final Map<String, String> extend,
                               final MenuResult defaultResult,
                               final ISmsProcessPublicInfoCallback callback) {
        final long start = System.currentTimeMillis();
        Log.i(TAG, "getCustomMenu: " + " phone=" + phone);
        ISmsPlugin.ISmsPluginCallback pluginCallback = new ISmsPlugin.ISmsPluginCallback() {
            @Override
            public void onCard(int status, int type, JSONObject obj) {
            }

            @Override
            public void onPublicInfo(int status, PublicInfoData data) {
                long end = System.currentTimeMillis();
                MenuResult customResult = new MenuResult().setData(data)
                        .setParseTime(end - start)
                        .setStatus(status);
                ISmsProcessPublicInfoCallback defaultCallback = null;
                if (callback != null) {
                    if (useDefaultMenu(phone, data)) {
                        defaultCallback = callback;
                    } else {
                        callback.onPublicInfo(phone, data);
                    }
                }

                if (defaultResult != null) {
                    reportCustomCapacity(defaultResult, customResult);
                } else if (!isCustomOnly(CarrierParamFacade.FUNC_MENU)) {
                    getDefaultMenu(phone, extend, customResult, defaultCallback);
                }
            }
        };

        if (SmsPluginManager.getInstance().getCustom() != null) {
            SmsPluginManager.getInstance().getCustom().parsePublicInfo(phone, extend, pluginCallback);
        }
    }

    /**
     * 异步获取企业资料/菜单
     *
     * @param phone
     * @param callback
     * @deprecated
     */
    public void parsePublicInfo(final String phone,
                                final ISmsProcessPublicInfoCallback callback) {
        parsePublicInfo(phone, null, callback);
    }

    /**
     * 异步获取企业资料/菜单
     *
     * @param phone
     * @param callback
     */
    public void parsePublicInfo(final String phone,
                                final Map<String, String> extend,
                                final ISmsProcessPublicInfoCallback callback) {
        if (!isParamReady(CarrierParamFacade.FUNC_MENU, phone)) {
            callback.onPublicInfo(phone, null);
            Log.w(TAG, "parsePublicInfo: param not ready");
            return;
        }

        sPubicInfoPool.submit(new Runnable() {
            @Override
            public void run() {
                if (useDefault(phone, CarrierParamFacade.FUNC_MENU, getCustomSelect(extend))
                        || SmsPluginManager.getInstance().getCustom() == null) {
                    getDefaultMenu(phone, extend, null, callback);
                } else {
                    getCustomMenu(phone, extend, null, callback);
                }
            }
        });
    }

    /**
     * 异步获取菜单（JSON）
     *
     * @param phone
     * @param extend
     * @param callBack
     */
    public void parseMenuInfo(final String phone, final Map<String, String> extend, final SdkCallBack callBack) {
        Log.v(TAG, "parseMenuInfo: " + phone);
        parsePublicInfo(phone,
                extend,
                new ISmsProcessPublicInfoCallback() {
                    @Override
                    public void onPublicInfo(String phone, PublicInfoData data) {
                        if (callBack != null) {
                            if (data != null) {
                                callBack.execute(data.manufacture, data.json);
                            } else {
                                callBack.execute(null, null);
                            }
                        }
                    }
                });
    }

    private boolean useDefaultMenu(String phone, PublicInfoData data) {
        if (isCustomOnly(CarrierParamFacade.FUNC_MENU)) {
            return false;
        }

        return (!mCarrierParamFacade.checkAdvancedWhiteList(phone)
                && (mCarrierParamFacade.checkBlackListSwitch(phone, CarrierParamFacade.FUNC_MENU) || (mCarrierParamFacade.checkOnResultNull(CarrierParamFacade.FUNC_MENU) && (data == null || data.menuList == null))));
    }

    /**
     * 处理菜单和按钮点击事件
     *
     * @param activity
     * @param action
     * @param extend
     */
    public void processAction(Activity activity, String action, Map<String, String> extend) {
        if (extend == null) {
            return;
        }

        String manufactureData = extend.get("ManufactureData");
        if (manufactureData.equalsIgnoreCase("XIAOYUAN")) {
            extend.remove("ManufactureData");
            SmsPluginManager.getInstance().getDefault().processAction(activity, action, extend);
        } else if (SmsPluginManager.getInstance().getCustom() != null) {
            SmsPluginManager.getInstance().getCustom().processAction(activity, action, extend);
        }
    }

    private SmsProcessFacade(Context context) {
        mContext = context;
        SmsPluginManager.getInstance(mContext);
        mCarrierParamFacade = CarrierParamFacade.getInstance(mContext);
        Log.i(TAG, "constructor end");
    }

    /**
     * 初始化融合SDK
     *
     * @param sdkDoAction
     * @param extend      e.g.
     *                    HashMap<String, String> extend = new HashMap<String, String>();
     *                    extend.put(Constant.ONLINE_UPDATE_SDK, "1");
     *                    extend.put(Constant.SUPPORT_NETWORK_TYPE, "2");
     *                    extend.put(Constant.SMARTSMS_ENHANCE, Boolean.TRUE.toString());
     *                    extend.put(Constant.CHANNEL, BuildConfig.DUOQU_SDK_CHANNEL);
     *                    extend.put(Constant.SECRETKEY, BuildConfig.DUOQU_SDK_CHANNEL_SECRETKEY);
     *                    extend.put(Constant.RSAPRVKEY, BuildConfig.DUOQU_SDK_RSAKEY);
     * @remark sdkDoAction 需要被正确实现
     */
    public void init(final AbsSdkDoAction sdkDoAction, final Map<String, String> extend) {
        mInitMap.putAll(extend);
        mDoAction = sdkDoAction;

        if (extend.containsKey(KEY_SAFE_CODE)) {
            mSafeCode = true;
        }

        if (extend.containsKey(KEY_PUBLIC_INFO)) {
            PublicInfoManager.getInstance(mContext);
        }

        SmsPluginManager.getInstance().getDefault().init(sdkDoAction, extend);
        sCachePool.execute(new Runnable() {
            @Override
            public void run() {
                // carrier data init use the context init by sdkinit
                CarrierParamFacade.getInstance().initData(extend);
                Log.i(TAG, "run: initData end");
            }
        });
    }

    private CarrierParamFacade mCarrierParamFacade = null;
    private static SmsProcessFacade sSmsProcessFacade = null;

    /**
     * 简化版构造函数
     *
     * @return
     */
    public static SmsProcessFacade getInstance() {
        return sSmsProcessFacade;
    }

    /**
     * 构造函数
     *
     * @param context
     * @return
     */
    public static SmsProcessFacade getInstance(Context context) {
        synchronized (SmsProcessFacade.class) {
            if (sSmsProcessFacade == null) {
                sSmsProcessFacade = new SmsProcessFacade(context);
            }
        }

        return sSmsProcessFacade;
    }

    /**
     * 将号码的识别结果从数据库加载到缓存
     *
     * @param phone
     * @note prefetch在主线程中调用以保障预取在实际获取之前；加载本身需要在工作线程中完成
     */
    public void loadCache(final String phone) {
        Log.i(TAG, "loadCache: prefetch");
        CarrierParamFacade.getInstance().prefetch(phone);
        sCachePool.execute(new Runnable() {
            @Override
            public void run() {
                SmsPluginManager.getInstance().getDefault().loadCache(phone);
                if (SmsPluginManager.getInstance().getCustom() != null) {
                    SmsPluginManager.getInstance().getCustom().loadCache(phone);
                }
            }
        });
    }

    /**
     * 后台识别，以提升性能
     *
     * @param phone null: 以短信接收时间倒序的所有信息; not null: 仅识别该号码下的短信
     * @param limit 一次识别的短信条数
     */
    public void backgroundParse(String phone, int limit) {
        Log.v(TAG, "backgroundParse: " + phone + " limit=" + limit);
        SmsPluginManager.getInstance().getDefault().backgroundParse(phone, limit);
        if (!isDefaultOnly(CarrierParamFacade.FUNC_CARD) && SmsPluginManager.getInstance().getCustom() != null) {
            SmsPluginManager.getInstance().getCustom().backgroundParse(phone, limit);
        }
    }

    /**
     * 清除缓存以释放RAM
     *
     * @param phone
     */
    public void clearCache(String phone) {
        Log.v(TAG, "clearCache: " + phone);
        SmsPluginManager.getInstance().getDefault().clearCache(phone);
        if (SmsPluginManager.getInstance().getCustom() != null) {
            SmsPluginManager.getInstance().getCustom().clearCache(phone);
        }
    }

    /**
     * 设置参数
     *
     * @param extend 同init的extend参数
     */
    public void setParam(Map<String, String> extend) {
        Log.i(TAG, "setParam: " + extend);
        mInitMap.putAll(extend);
        SmsPluginManager.getInstance().getDefault().setParam(extend);
        if (SmsPluginManager.getInstance().getCustom() != null) {
            SmsPluginManager.getInstance().getCustom().setParam(extend);
        }
    }
}

